#include "RTC_Config.h"

// 定义并初始化RTC时间数组，格式为：年、月、日、时、分、秒
// 数组索引：0-年，1-月，2-日，3-时，4-分，5-秒
u16 RTC_Time[] = {2025, 5, 10, 13, 45, 10};

/**
    ******************************************************************************
    * 函数名称：RTC_LSE_Config
    * 功能描述：配置RTC使用LSE（低速外部时钟，通常为32.768kHz晶振）
    * 输入参数：无
    * 返回值  ：无
    * 详细说明：
    *   1. 首先检查备份寄存器BKP_DR1的值是否为0xA5A5，如果不是则进行RTC初始化
    *   2. 初始化包括：启用LSE、设置RTC时钟源、配置RTC预分频器等
    *   3. 如果已经初始化过，则跳过初始化过程
    * 
    * 硬件要求：
    *   - 需要连接32.768kHz晶振
    *   - 需要启用PWR和BKP外设时钟
    ******************************************************************************
**/
void RTC_LSE_Config(void)
{
    u8 temp = 0;  // 定义超时计数器，用于LSE启动等待

    // 使能PWR(电源控制)和BKP(备份寄存器)的APB1总线时钟
    // RCC_APB1PeriphClockCmd是库函数，用于使能APB1总线上的外设时钟
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR | RCC_APB1Periph_BKP, ENABLE);
    
    // 允许访问备份寄存器区域（写保护解除）
    // 在对备份寄存器操作前必须调用此函数
    PWR_BackupAccessCmd(ENABLE);

    // 读取备份寄存器BKP_DR1的值，判断RTC是否已经配置过
    // 0xA5A5是自定义的标记值，用于标识RTC已初始化
    if(BKP_ReadBackupRegister(BKP_DR1) != 0xA5A5){
        // 通过串口输出调试信息
		USART1_Printf("\r\n RTC has not been configured yet");

        // 复位备份寄存器到默认值
        // 这会清除所有备份寄存器的内容
        BKP_DeInit();

        // 开启LSE振荡器（外部低速晶振，通常为32.768kHz）
        RCC_LSEConfig(RCC_LSE_ON);

        // 等待LSE振荡器就绪，带有超时检测
        // RCC_FLAG_LSERDY标志位为1表示LSE就绪
        while(RCC_GetFlagStatus(RCC_FLAG_LSERDY) == RESET) {
            // 超时计数器递增
            if(++temp > 250) {
                // 超过2.5秒（250*10ms）仍未就绪，输出错误信息并返回
                USART1_Printf("\r\nLSE Init Failed!");
                return;  // 初始化失败，直接退出函数
            }
            Delay_ms(10);  // 每次循环等待10ms
        }

        // 设置RTC时钟源为LSE
        // RTC可以使用LSE、LSI或HSE分频作为时钟源
        RCC_RTCCLKConfig(RCC_RTCCLKSource_LSE);
        
        // 使能RTC时钟
        RCC_RTCCLKCmd(ENABLE);

        // 等待RTC寄存器与APB时钟同步
        // 由于RTC时钟较慢，需要等待同步后才能操作寄存器
        RTC_WaitForSynchro();
        // 等待上一次对RTC寄存器的操作完成
        RTC_WaitForLastTask();

        // 设置RTC预分频器，使RTC时钟为1Hz
        // LSE通常为32768Hz，32768/(32767+1)=1Hz
        RTC_SetPrescaler(32768 - 1);
        // 等待预分频器设置完成
        RTC_WaitForLastTask();

        // 输出配置成功信息
        USART1_Printf("\r\n The RTC configuration was successful");

        // 调用函数设置初始时间（使用全局变量RTC_Time的值）
        RTC_Set_Time();

        // 在备份寄存器BKP_DR1中写入标志值0xA5A5
        // 下次上电时将检测此值来判断是否需要重新配置
        BKP_WriteBackupRegister(BKP_DR1, 0xA5A5);
    }
    else{
        // 如果RTC已经配置过，检查复位来源

        // 检测是否发生了上电复位
        if(RCC_GetFlagStatus(RCC_FLAG_PORRST)){
            USART1_Printf("\r\n Power-on reset occurs");
        }
        // 检测是否发生了引脚复位（NRST引脚）
        else if(RCC_GetFlagStatus(RCC_FLAG_PINRST)){
            USART1_Printf("\r\n Pin reset");
        }

        // 输出无需重新配置的信息
        USART1_Printf("\r\n No RTC configuration is required");

        // 仍然需要等待RTC寄存器同步
        RTC_WaitForSynchro();
        RTC_WaitForLastTask();
    }

    // 如果定义了RTC时钟输出使能宏
    #ifdef RTCClockOutput_Enable
        // 禁用侵入检测功能（Tamper pin）
        // 通常在启用RTC时钟输出时需要禁用侵入检测
        BKP_TamperPinCmd(DISABLE);
    #endif

    // 清除所有复位标志位
    // 包括PORRST、PINRST等
    RCC_ClearFlag();
}

/**
    ******************************************************************************
    * 函数名称：NVIC_Config
    * 功能描述：配置RTC中断的NVIC设置
    * 输入参数：无
    * 返回值  ：无
    * 详细说明：
    *   1. 配置RTC中断通道，优先级为1
    * 
    * 硬件要求：
    *   - 无特殊要求
    ******************************************************************************
**/
void NVIC_Config(void)
{
    // 定义NVIC初始化结构体
    NVIC_InitTypeDef NVIC_InitStructure;

    // 设置NVIC优先级分组为组1
    // 组1表示：1位抢占优先级，3位子优先级
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);

    // 配置RTC中断通道
    NVIC_InitStructure.NVIC_IRQChannel = RTC_IRQn;  // 中断通道设为RTC
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;  // 抢占优先级为1
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;         // 子优先级为0
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;            // 使能该中断通道
    NVIC_Init(&NVIC_InitStructure);  // 应用NVIC配置
}

/**
    ******************************************************************************
    * 函数名称：EXTI_Config
    * 功能描述：配置RTC闹钟中断的EXTI线
    * 输入参数：无
    * 返回值  ：无
    * 详细说明：
    *   1. 配置EXTI线17（RTC闹钟中断线）为上升沿触发
    * 
    * 硬件要求：
    *   - 需要启用AFIO时钟
    ******************************************************************************
**/
void EXTI_Config(void)
{
    // 定义EXTI初始化结构体
    EXTI_InitTypeDef EXTI_InitStructure;

    // 使能AFIO(复用功能IO)时钟
    // 配置EXTI需要先使能AFIO时钟
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);

    // 清除EXTI线17的中断挂起位（如果有）
    EXTI_ClearITPendingBit(EXTI_Line17);
    // 配置EXTI线17
    EXTI_InitStructure.EXTI_Line = EXTI_Line17;       // 选择EXTI线17（对应RTC闹钟）
    EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt; // 设置为中断模式
    EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising; // 上升沿触发
    EXTI_InitStructure.EXTI_LineCmd = ENABLE;           // 使能该EXTI线
    EXTI_Init(&EXTI_InitStructure);  // 应用EXTI配置
}

/**
    ******************************************************************************
    * 函数名称：RTC_IRQHandler
    * 功能描述：RTC中断服务函数
    * 输入参数：无
    * 返回值  ：无
    * 详细说明：
    *   1. 当前为空实现，可根据需要添加秒中断处理逻辑
    * 
    * 硬件要求：
    *   - 无特殊要求
    ******************************************************************************
**/
void RTC_IRQHandler(void)
{
    // 示例代码中为空实现
    // 实际使用时可以取消以下注释来处理秒中断
    /*
    if(RTC_GetITStatus(RTC_IT_SEC))  // 检查是否是秒中断
    {
        RTC_ClearITPendingBit(RTC_IT_SEC);  // 清除秒中断标志
        EXTI_ClearITPendingBit(EXTI_Line17); // 清除EXTI线17的中断标志
    }
    */
}

/**
    ******************************************************************************
    * 函数名称：RTC_Set_Time
    * 功能描述：设置RTC时间
    * 输入参数：无（使用全局变量RTC_Time作为时间源）
    * 返回值  ：无
    * 详细说明：
    *   1. 将RTC_Time数组中的时间转换为time_t格式
    *   2. 考虑时区偏移（-8小时，可能是北京时间）
    *   3. 设置RTC计数器值
    * 
    * 硬件要求：
    *   - RTC必须已初始化
    ******************************************************************************
**/
void RTC_Set_Time(void)
{
    time_t time_cnt;      // 定义time_t类型变量存储时间戳
    struct tm time_date;  // 定义tm结构体存储分解的时间

    // 填充tm结构体，将全局变量RTC_Time的值转换为标准tm结构
    time_date.tm_year = RTC_Time[0] - 1900;  // 年份，从1900开始计数
    time_date.tm_mon  = RTC_Time[1] - 1;     // 月份，0-11（1月为0）
    time_date.tm_mday = RTC_Time[2];         // 日
    time_date.tm_hour = RTC_Time[3];         // 时
    time_date.tm_min  = RTC_Time[4];         // 分
    time_date.tm_sec  = RTC_Time[5];         // 秒

    // 将tm结构转换为time_t时间戳，并减去8小时（时区调整，可能是北京时间）
    // mktime函数会自动处理tm结构中的非法值（如2月30日）
    time_cnt = mktime(&time_date) - 8 * 60 * 60;

    // 设置RTC计数器值为计算得到的时间戳
    // RTC内部实际上是一个32位计数器，每秒递增1
    RTC_SetCounter(time_cnt);
    // 等待设置完成
    RTC_WaitForLastTask();
}


/**
    ******************************************************************************
    * 函数名称：RTC_Read_Time
    * 功能描述：从RTC读取当前时间
    * 输入参数：无
    * 返回值  ：无（结果存储在全局变量RTC_Time中）
    * 详细说明：
    *   1. 读取RTC计数器值
    *   2. 加上8小时时区偏移
    *   3. 转换为年月日时分秒格式
    * 
    * 硬件要求：
    *   - RTC必须已初始化
    ******************************************************************************
**/
void RTC_Read_Time(void)
{
    time_t time_cnt;      // 定义time_t类型变量存储时间戳
    struct tm time_date;  // 定义tm结构体存储分解的时间

    // 获取RTC计数器当前值，并加上8小时（时区调整）
    time_cnt = RTC_GetCounter() + 8 * 60 * 60;

    // 将时间戳转换为本地时间（存储在tm结构中）
    // localtime函数考虑了时区和夏令时
    time_date = *localtime(&time_cnt);

    // 将转换后的时间存储到全局变量RTC_Time中
    RTC_Time[0] = time_date.tm_year + 1900;  // 年份转换（tm_year是从1900开始的）
    RTC_Time[1] = time_date.tm_mon + 1;      // 月份转换（0-11 → 1-12）
    RTC_Time[2] = time_date.tm_mday;         // 日
    RTC_Time[3] = time_date.tm_hour;         // 时
    RTC_Time[4] = time_date.tm_min;          // 分
    RTC_Time[5] = time_date.tm_sec;          // 秒
}

/**
    ******************************************************************************
    * 函数名称：Time_Display
    * 功能描述：通过串口显示当前RTC时间和相关寄存器值
    * 输入参数：无
    * 返回值  ：无
    * 详细说明：
    *   1. 读取当前RTC时间
    *   2. 通过USART1输出日期、时间、计数器值和分频器值
    * 
    * 硬件要求：
    *   - USART1必须已初始化
    *   - RTC必须已初始化
    ******************************************************************************
**/
void Time_Display(void)
{
    // 首先读取当前RTC时间到全局变量RTC_Time
    RTC_Read_Time();

    // 通过串口输出日期信息
    USART1_Printf("\r\n/*******Date*******/\r\n");
    // 格式化输出年(4位) 月(2位) 日(2位)
    USART1_Printf("%04d %02d %02d\r\n", RTC_Time[0], RTC_Time[1], RTC_Time[2]);

    // 通过串口输出时间信息
    USART1_Printf("/*******Time*******/\r\n");
    // 格式化输出时(2位) 分(2位) 秒(2位)
    USART1_Printf("%02d %02d %02d", RTC_Time[3], RTC_Time[4], RTC_Time[5]);

    // 输出RTC计数器当前值
    USART1_Printf("\r\n/*******CNT*******/\r\n");
    // USART1_SendNumber函数用于输出数字，第二个参数10表示十进制
    USART1_SendNumber(RTC_GetCounter(), 10);

    // 输出RTC预分频器当前值
    USART1_Printf("\r\n/*******DIV*******/\r\n");
    USART1_SendNumber(RTC_GetDivider(), 10);

    // 输出换行符使显示更整齐
    USART1_Printf("\r\n");
}
